package com.iuhao.web.core.utils;

import com.alibaba.fastjson.JSON;
import org.apache.commons.lang3.StringUtils;
import org.apache.http.*;
import org.apache.http.auth.AuthScope;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.CredentialsProvider;
import org.apache.http.client.HttpRequestRetryHandler;
import org.apache.http.client.ResponseHandler;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.conn.HttpHostConnectException;
import org.apache.http.conn.routing.HttpRoute;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.*;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.util.EntityUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.net.ssl.SSLException;
import javax.net.ssl.SSLHandshakeException;
import java.io.InterruptedIOException;
import java.net.ConnectException;
import java.net.UnknownHostException;

/**
 * Created by yanhao on 2017/11/28.
 */
public class HttpRequestUtils {
    private static Logger logger = LoggerFactory.getLogger(HttpRequestUtil.class);
    private static final int TIMEOUT = 150000;
    private static CloseableHttpClient httpClient = null;
    private static final Object syncLock = new Object();

    public HttpRequestUtils() {
    }

    private static void config(HttpRequestBase httpRequestBase) {
        RequestConfig requestConfig = RequestConfig.custom().setConnectionRequestTimeout(150000).setConnectTimeout(150000).setSocketTimeout(150000).build();
        httpRequestBase.setConfig(requestConfig);
    }

    private static HttpClientContext createBasicAuthContext(CredentialsProvider credentialsProvider, String url) {
        BasicAuthCache authCache = new BasicAuthCache();
        BasicScheme basicAuth = new BasicScheme();
        authCache.put(getHttpHost(url), basicAuth);
        HttpClientContext context = HttpClientContext.create();
        context.setCredentialsProvider(credentialsProvider);
        context.setAuthCache(authCache);
        return context;
    }

    private static HttpHost getHttpHost(String url) {
        String hostname = url.split("/")[2];
        int port = 80;
        if(hostname.contains(":")) {
            String[] httpHost = hostname.split(":");
            hostname = httpHost[0];
            port = Integer.parseInt(httpHost[1]);
        }

        HttpHost httpHost1 = new HttpHost(hostname, port);
        return httpHost1;
    }

    public static CredentialsProvider getBasicCredentialsProvider(String url, String username, String password) {
        BasicCredentialsProvider credsProvider = new BasicCredentialsProvider();
        UsernamePasswordCredentials defaultCreds = new UsernamePasswordCredentials(username, password);
        HttpHost host = getHttpHost(url);
        credsProvider.setCredentials(new AuthScope(host.getHostName(), host.getPort()), defaultCreds);
        return credsProvider;
    }

    public static CloseableHttpClient getHttpClient(String url) {
        if(httpClient == null) {
            Object var1 = syncLock;
            synchronized(syncLock) {
                if(httpClient == null) {
                    httpClient = createHttpClient(200, 40, 100, getHttpHost(url));
                }
            }
        }

        return httpClient;
    }

    private static CloseableHttpClient createHttpClient(int maxTotal, int maxPerRoute, int maxRoute, HttpHost httpHost) {
        PlainConnectionSocketFactory plainsf = PlainConnectionSocketFactory.getSocketFactory();
        SSLConnectionSocketFactory sslsf = SSLConnectionSocketFactory.getSocketFactory();
        Registry registry = RegistryBuilder.create().register("http", plainsf).register("https", sslsf).build();
        PoolingHttpClientConnectionManager cm = new PoolingHttpClientConnectionManager(registry);
        cm.setMaxTotal(maxTotal);
        cm.setDefaultMaxPerRoute(maxPerRoute);
        cm.setMaxPerRoute(new HttpRoute(httpHost), maxRoute);
        HttpRequestRetryHandler httpRequestRetryHandler = (exception, executionCount, context) -> {
            if(executionCount >= 5) {
                return false;
            } else if(exception instanceof NoHttpResponseException) {
                return true;
            } else if(exception instanceof SSLHandshakeException) {
                return false;
            } else if(exception instanceof InterruptedIOException) {
                return false;
            } else if(exception instanceof UnknownHostException) {
                return false;
            } else if(exception instanceof ConnectTimeoutException) {
                return false;
            } else if(exception instanceof SSLException) {
                return false;
            } else {
                HttpClientContext clientContext = HttpClientContext.adapt(context);
                HttpRequest request = clientContext.getRequest();
                return !(request instanceof HttpEntityEnclosingRequest);
            }
        };
        HttpClientBuilder clientBuilder = HttpClients.custom();
        clientBuilder.setConnectionManager(cm).setRetryHandler(httpRequestRetryHandler);
        CloseableHttpClient httpClient = clientBuilder.build();
        return httpClient;
    }

    public static <T> T doPost(String url, String json, Class<T> clz) throws Exception {
        String result = doPost(url, json);
        return JSON.parseObject(result, clz);
    }

    public static String doPostWithCredentials(String url, String jsonStr, CredentialsProvider credentialsProvider) throws Exception {
        try {
            CloseableHttpClient e = getHttpClient(url);
            HttpPost post = new HttpPost(url);
            config(post);
            logger.info("Executing request: " + post.getRequestLine());
            StringEntity responseBody;
            if(!StringUtils.isEmpty(jsonStr)) {
                responseBody = new StringEntity(jsonStr, "UTF-8");
                responseBody.setContentEncoding("UTF-8");
                responseBody.setContentType("application/json");
                post.setEntity(responseBody);
            }

            responseBody = null;
            String responseBody1;
            if(credentialsProvider != null) {
                HttpClientContext clientContext = createBasicAuthContext(credentialsProvider, url);
                responseBody1 = (String)e.execute(post, getStringResponseHandler(url), clientContext);
            } else {
                responseBody1 = (String)e.execute(post, getStringResponseHandler(url));
            }

            return responseBody1;
        } catch (Exception var7) {
            if(!(var7 instanceof HttpHostConnectException) && !(var7.getCause() instanceof ConnectException)) {
                logger.error("HttpRequestUtil.doPost: " + var7.getMessage(), var7);
                return null;
            } else {
                throw new ConnectException("====> 连接服务器" + url + "失败： " + var7.getMessage());
            }
        }
    }

    public static String doPost(String url, String json) throws Exception {
        return doPostWithCredentials(url, json, (CredentialsProvider)null);
    }

    private static ResponseHandler<String> getStringResponseHandler(String url) {
        return (response) -> {
            int status = response.getStatusLine().getStatusCode();
            if(status >= 200 && status < 300) {
                HttpEntity entity = response.getEntity();
                return entity != null? EntityUtils.toString(entity, "UTF-8"):null;
            } else {
                logger.error("Unexpected response from request: " + url + "  response status: " + status);
                throw new ClientProtocolException("Unexpected response status: " + status);
            }
        };
    }

    public static <T> T doGet(String url, Class<T> clz) throws Exception {
        String result = doGet(url);
        return JSON.parseObject(result, clz);
    }

    public static String doGetWithCredentials(String url, CredentialsProvider credentialsProvider) throws Exception {
        try {
            CloseableHttpClient e = getHttpClient(url);
            HttpGet httpget = new HttpGet(url);
            config(httpget);
            logger.info("Executing request: " + httpget.getRequestLine());
            String responseBody = null;
            if(credentialsProvider != null) {
                HttpClientContext clientContext = createBasicAuthContext(credentialsProvider, url);
                responseBody = (String)e.execute(httpget, getStringResponseHandler(url), clientContext);
            } else {
                responseBody = (String)e.execute(httpget, getStringResponseHandler(url));
            }

            return responseBody;
        } catch (Exception var6) {
            if(!(var6 instanceof HttpHostConnectException) && !(var6.getCause() instanceof ConnectException)) {
                logger.error("HttpRequestUtil.doGet: " + var6.getMessage(), var6);
                return null;
            } else {
                throw var6;
            }
        }
    }

    public static String doGet(String url) throws Exception {
        return doGetWithCredentials(url, (CredentialsProvider)null);
    }
}
